package com.es.test.utils;


import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.RangeQuery;
import co.elastic.clients.elasticsearch.core.CountResponse;
import co.elastic.clients.elasticsearch.core.GetResponse;
import co.elastic.clients.elasticsearch.core.ScrollResponse;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.json.JsonData;
import com.alibaba.fastjson2.JSON;
import com.es.test.query.Field;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.*;

/**
 * @author django
 * @date 2024/2/6
 * @description TODO
 */
public class ElasticClientUtils<T> {

    /**
     * @param
     * @param client
     * @param dto
     * @param target
     * @return java.util.List<T>
     * @author django.jiang
     * @description 根据关键字查询
     * @date 2024/2/16 17:15
     */
    public List<T> queryByFiled(ElasticsearchClient client, EsQueryDTO dto, Class<T> target) throws Exception {
        List<T> result = new ArrayList<>();
        List<SortOptions> sorts = new ArrayList<>();
        if (StringUtils.isNotBlank(dto.getOrder())) {
            SortOptions sortOptions = SortOptions.of(s -> s.field(f -> f.field(dto.getOrder()).order(SortOrder.valueOf(dto.getOrderType()))));
            sorts.add(sortOptions);
        }
        SearchResponse<HashMap> search = client.search(s -> s
                        .index(dto.getIndexName())
                        .query(q -> q.term(t -> t
                                .field(dto.getField())
                                .value(dto.getWord())
                        )).sort(sorts),
                HashMap.class);
        return getResult(target, result, search);
    }

    /**
     * @param
     * @param client
     * @param dto
     * @param target
     * @return java.util.List<T>
     * @author django.jiang
     * @description 根据关键字查询，基于游标查询scroll
     * @date 2024/2/16 17:15
     */
    public List<T> queryByFields(ElasticsearchClient client, EsQueryDTO dto, List<Query> queries, Class<T> target) throws Exception {
        List<T> result = new ArrayList<>();
        List<SortOptions> sorts = new ArrayList<>();
        if (StringUtils.isNotBlank(dto.getOrder())) {
            SortOptions sortOptions = SortOptions.of(s -> s.field(f -> f.field(dto.getOrder()).order(SortOrder.valueOf(dto.getOrderType()))));
            sorts.add(sortOptions);
        }
        getFieldValues(dto, queries);
        //使用scroll深度分页查询
        SearchResponse<HashMap> search = client.search(s -> s
                        .index(dto.getIndexName()).query(q -> q.bool(b -> b.must(queries))).size(5000).scroll(t -> t.time("5s"))
                        .sort(sorts),
                HashMap.class);
        StringBuffer scrollId = new StringBuffer(search.scrollId());
        //循环查询，直到查不到数据

        ScrollResponse<HashMap> scrollSearch = null;
        do {
            getResult(target, result, search);
            StringBuffer finalScrollId = scrollId;
            scrollSearch = client.scroll(s -> s.scrollId(finalScrollId.toString()).scroll(t -> t.time("5s")), HashMap.class);
            scrollId = new StringBuffer(search.scrollId());
        } while (!scrollSearch.hits().hits().isEmpty());
        //getResult(target, result, search)
        return result;
    }

    /**
     * @param
     * @param client
     * @param dto
     * @param target
     * @return java.util.List<T>
     * @author django.jiang
     * @description 根据关键字分页查询
     * @date 2024/2/16 17:15
     */
    public List<T> queryByFiledWithPage(ElasticsearchClient client, EsQueryDTO dto, Class<T> target) throws Exception {
        List<T> result = new ArrayList<>();
        List<SortOptions> sorts = new ArrayList<>();
        if (StringUtils.isNotBlank(dto.getOrder())) {
            SortOptions sortOptions = SortOptions.of(s -> s.field(f -> f.field(dto.getOrder()).order(SortOrder.valueOf(dto.getOrderType()))));
            sorts.add(sortOptions);
        }
        SearchResponse<HashMap> search = client.search(s -> s
                        .index(dto.getIndexName())
                        .query(q -> q.term(t -> t
                                .field(dto.getField())
                                .value(dto.getWord())
                        )).sort(sorts).from(dto.getFrom()).size(dto.getSize()),
                HashMap.class);
        return getResult(target, result, search);
    }


    private List<T> getResult(Class<T> target, List<T> result, SearchResponse<HashMap> search) {
        List<Hit<HashMap>> hits = search.hits().hits();
        Iterator<Hit<HashMap>> iterator = hits.iterator();
        while (iterator.hasNext()) {
            Hit<HashMap> decodeBeanHit = iterator.next();
            Map<String, Object> docMap = decodeBeanHit.source();
            docMap.put("id", decodeBeanHit.id());
            String json = JSON.toJSONString(docMap);
            T obj = JSON.parseObject(json, target);
            result.add(obj);
        }
        return result;
    }

    /**
     * @param
     * @param client
     * @param dto
     * @return long
     * @author django.jiang
     * @description 根据关键字查询总条数
     * @date 2024/2/16 17:15
     */
    public long queryCountByFiled(ElasticsearchClient client, EsQueryDTO dto) throws Exception {
        CountResponse count = client.count(c -> c.index(dto.getIndexName()).query(q -> q.term(t -> t
                .field(dto.getField())
                .value(dto.getWord())
        )));
        long total = count.count();
        return total;
    }


    /**
     * @return com.es.test.utils.PageData<T>
     * @author django.jiang
     * @description 查询分页信息
     * @date 2024/2/16 17:16
     */
    public PageData<T> queryPageByFiled(ElasticsearchClient client, EsQueryDTO dto, Class<T> target) throws Exception {
        long total = queryCountByFiled(client, dto);
        List<T> result = queryByFiledWithPage(client, dto, target);
        PageData<T> pageData = new PageData<>(result, total);
        return pageData;
    }

    /**
     * @return com.es.test.utils.PageData<T>
     * @author django.jiang
     * @description 查询分页信息-复合查询
     * @date 2024/2/16 17:16
     */
    public PageData<T> queryPageByFiled(ElasticsearchClient client, EsQueryDTO dto, List<Query> queries, Class<T> target) throws Exception {
        if (null == queries) {
            queries = new ArrayList<>();
        }
        long total = queryCountByFields(client, dto, queries);
        if (total > 10000) {
            total = 10000;
        }
        List<T> result = queryByFieldsWithPage(client, dto, queries, target);
        PageData<T> pageData = new PageData<>(result, total);
        return pageData;
    }

    /**
     * @param
     * @param client
     * @param dto
     * @return long
     * @author django.jiang
     * @description 根据关键字查询总条数-复合查询
     * @date 2024/2/16 17:15
     */
    public long queryCountByFields(ElasticsearchClient client, EsQueryDTO dto, List<Query> queries) throws Exception {
        getFieldValues(dto, queries);
        CountResponse count = client.count(c -> c.index(dto.getIndexName()).query(q -> q.bool(b -> b.must(queries))));
        long total = count.count();
        return total;
    }


    /**
     * @return java.util.List<T>
     * @author django.jiang
     * @description 根据关键字分页查询- 复合查询
     * @date 2024/2/16 17:15
     */
    public List<T> queryByFieldsWithPage(ElasticsearchClient client, EsQueryDTO dto, List<Query> queries, Class<T> target) throws Exception {
        List<T> result = new ArrayList<>();
        List<SortOptions> sorts = new ArrayList<>();
        if (StringUtils.isNotBlank(dto.getOrder())) {
            SortOptions sortOptions = SortOptions.of(s -> s.field(f -> f.field(dto.getOrder()).order(SortOrder.valueOf(dto.getOrderType()))));
            sorts.add(sortOptions);
        }
        SearchResponse<HashMap> search = client.search(s -> s
                        .index(dto.getIndexName())
                        .query(q -> q.bool(b -> b.must(queries)))
                        .sort(sorts).from(dto.getFrom()).size(dto.getSize()),
                HashMap.class);
        return getResult(target, result, search);
    }

    public List<T> queryMany(ElasticsearchClient client, EsQueryDTO dto, List<Query> queries, Class<T> target) throws IOException {
        String searchText = "bike";
        double maxPrice = 200.0;

// Search by product name
        Query byName = MatchQuery.of(m -> m
                .field("name")
                .query(searchText)
        )._toQuery();

// Search by max price
        Query byMaxPrice = RangeQuery.of(r -> r
                .field("price")
                .gte(JsonData.of(maxPrice))
        )._toQuery();

// Combine name and price queries to search the product index
        SearchResponse<Map> response = client.search(s -> s
                        .index("products")
                        .query(q -> q
                                .bool(b -> b
                                        .must(byName)
                                        .must(byMaxPrice)
                                )
                        ),
                Map.class
        );
        queries.stream().forEach(item -> BoolQuery.of(builder -> builder.must(item)));
        List<Field> list = new ArrayList<>();
        list.stream().forEach(item -> Query.of(builder -> builder.term(q-> q.field(item.getFieldName()).queryName(""))));

        client.search(s -> s.index("user_label").query(q -> q
                .bool(b -> b
                        .must(byName)
                        .must(byMaxPrice)
                )
        ), Map.class);

        List<Hit<Map>> hits = response.hits().hits();
        List<T> result = new ArrayList<>();
        for (Hit<Map> hit : hits) {
            Map product = hit.source();
            result.add((T) product);
        }
        return  result;
    }

    /**
     * 构件复合查询条件
     *
     * @param dto
     * @param queries
     */
    private void getFieldValues(EsQueryDTO dto, List<Query> queries) {
        List<FieldValue> fieldValues = new ArrayList<>();
        //根据关键字列表构件复合查询的值
        dto.getWords().stream().forEach(word -> fieldValues.add(FieldValue.of(word)));
        //查询条件列表
        queries.add(Query.of(q -> q.terms(t -> t.field(dto.getField()).terms(v -> v.value(fieldValues)))));
    }


    /**
     * @param
     * @param client
     * @param dto
     * @param target
     * @return java.lang.Object
     * @author django.jiang
     * @description 根据文档id查询
     * @date 2024/2/16 17:16
     */
    public T queryByDocumentId(ElasticsearchClient client, EsQueryDTO dto, Class<T> target) throws Exception {
        GetResponse<HashMap> getResponse = client.get(s -> s
                        .index(dto.getIndexName()).id(dto.getWord()),
                HashMap.class);
        getResponse.source();
        Map<String, Object> docMap = getResponse.source();
        String json = JSON.toJSONString(docMap);
        T obj = JSON.parseObject(json, target);
        return obj;
    }

}